﻿using Microsoft.Xna.Framework.Net;
using OilRigSumoWrestlers.Controllables.Controllers;
using OilRigSumoWrestlers.Entities;
using RaptorEngine;
using RaptorEngine.Components;

namespace OilRigSumoWrestlers.NetworkTools
{
    public class SumoNetwork : RaptorEngine.Network.NetworkTools 
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="SumoNetwork" /> class.
        /// </summary>
        /// <param name="maxLocalGamers">The max local number of gamers.</param>
        /// <param name="maxNetworkGamers">The max network number of gamers.</param>
        public SumoNetwork(int maxLocalGamers, int maxNetworkGamers) : base(maxLocalGamers, maxNetworkGamers)
        {
            MAX_LOCAL_GAMERS = maxLocalGamers;
            MAX_NETWORK_GAMERS = maxNetworkGamers;
        }

        /// <summary>
        /// Sets the gamers tags to playercharacters.
        /// </summary>
        /// <param name="p1">Player 1.</param>
        /// <param name="p2">Player 2.</param>
        public void SetGamers(PlayerCharacter p1, PlayerCharacter p2)
        {
            GameEngine.Instance.Network.Gamers[0].Tag = p1;
            GameEngine.Instance.Network.Gamers[1].Tag = p2;
        }

        #region Writers
        /// <summary>
        /// Custom packetwriter that writes the player/gamer data.
        /// </summary>
        /// <param name="entity">The entity.</param>
        private void PlayerWriter(Entity entity)
        {
            // Packettype.
            PacketWriter.Write("Player");
            // Push.
            PacketWriter.Write(((NetworkControllable)entity.Components[ComponentType.Controllable]).Pushed);
            // Last input.
            PacketWriter.Write(((NetworkControllable)entity.Components[ComponentType.Controllable]).LastMove);
            // Acceleration.
            PacketWriter.Write(((Movable)entity.Components[ComponentType.Movable]).Acceleration);
            // Velocity.
            PacketWriter.Write(((Movable)entity.Components[ComponentType.Movable]).Velocity);
            // Position.
            PacketWriter.Write(entity.Position);
            
            // Reset the local representation push state.
            ((NetworkControllable) entity.Components[ComponentType.Controllable]).Pushed = false;
        }
        #endregion

        #region Readers

        /// <summary>
        /// Custom packetreader that reads in the player/gamer data.
        /// </summary>
        /// <param name="entity">The entity.</param>
        /// <returns></returns>
        private Entity PlayerReader(Entity entity)
        {
            if (PacketReader.Length > 0)
            {
                // Checks the push.
                ((NetworkOpponentControllable)entity.Components[ComponentType.Controllable]).Pushed = PacketReader.ReadBoolean();
                // Gets the last input.
                ((NetworkOpponentControllable)entity.Components[ComponentType.Controllable]).LastMove = PacketReader.ReadVector2();
                // Gets the acceleration.
                ((Movable)entity.Components[ComponentType.Movable]).Acceleration = PacketReader.ReadVector3();
                // Gets the velocity.
                ((Movable) entity.Components[ComponentType.Movable]).Velocity = PacketReader.ReadVector3();
                // Gets the position.
                entity.Position = PacketReader.ReadVector3();

                return entity;  
            }
            return entity;
        }
        #endregion

        /// <summary>
        /// The packet writer. Handles the writing of packets to a specific gamer.
        /// </summary>
        /// <param name="writer">The packetwriter.</param>
        /// <param name="gamer">The gamer.</param>
        public override void NetPacketWriter(PacketWriter writer, LocalNetworkGamer gamer)
        {
            PacketWriter = writer;

            // Go through all the networkable entities and send packets.
            foreach (Entity entity in GameEngine.Instance.Scene.PeekScene().GetNetworkableEntitys())
            {
                foreach (Component component in entity.Components.Values)
                {
                    if (component.ComponentType == ComponentType.Movable)
                    {
                        PlayerWriter(entity);
                        gamer.SendData(PacketWriter, SendDataOptions.InOrder);
                    }
                }
            }
        }

        /// <summary>
        /// The packet reader. Handles the reading of incoming packets to a specific gamer.
        /// </summary>
        /// <param name="reader">The packetreader.</param>
        /// <param name="gamer">The gamer.</param>
        public override void NetPacketReader(PacketReader reader, LocalNetworkGamer gamer)
        {
            PacketReader = reader;

            while (gamer.IsDataAvailable)
            {
                // The sender.
                NetworkGamer sender;

                // Read single packet.
                gamer.ReceiveData(PacketReader, out sender);

                // Discard packets from local gamers.
                if (sender.IsLocal)
                    continue;

                // Read wich packettype it is.
                string packetType = PacketReader.ReadString();

                // Get the characters from the gamers tags.
                PlayerCharacter p1 = GameEngine.Instance.Network.Gamers[0].Tag as PlayerCharacter;
                PlayerCharacter p2 = GameEngine.Instance.Network.Gamers[1].Tag as PlayerCharacter;

                // Go through the entities and check for a match and then act accordingly.
                foreach (Entity entity in GameEngine.Instance.Scene.PeekScene().Entities)
                {
                    if (packetType == "Player")
                    {
                        if (entity.Id == p1.Id && !GameEngine.Instance.Network.CheckLocal(0))
                        {
                            p1 = (PlayerCharacter) PlayerReader(entity);
                        }
                        if (entity.Id == p2.Id && !GameEngine.Instance.Network.CheckLocal(1))
                        {
                            p2 = (PlayerCharacter) PlayerReader(entity);
                        }
                    }
                }
                // Set the updated entities back to the gamers tags.
                GameEngine.Instance.Network.Gamers[0].Tag = p1;
                GameEngine.Instance.Network.Gamers[1].Tag = p2;
            }
        }
    }
}
